package cc.cix.gt.org.service.impl;

import cc.cix.gt.common.entity.MenuTree;
import cc.cix.gt.common.entity.ResultPage;
import cc.cix.gt.common.entity.Tree;
import cc.cix.gt.common.util.SystemUtil;
import cc.cix.gt.common.util.TreeUtil;
import cc.cix.gt.org.dto.query.QueryMenuReq;
import cc.cix.gt.org.entity.Menu;
import cc.cix.gt.org.entity.RoleMenu;
import cc.cix.gt.org.entity.UserRole;
import cc.cix.gt.org.mapper.MenuMapper;
import cc.cix.gt.org.service.IMenuService;
import cc.cix.gt.org.service.IRoleMenuService;
import cc.cix.gt.org.service.IUserRoleService;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author cix
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu>
        implements IMenuService {

    private final IRoleMenuService roleMenuService;
    private final IUserRoleService userRoleService;

    @Override
    public List<String> listUserPermission() {
        Long userId = SystemUtil.getCurrentUser().getId();
        List<Long> menuIds = listMenuIdByUserId(userId);

        return lambdaQuery().in(Menu::getId, menuIds).ne(Menu::getPerms, "")
                .list()
                .stream()
                .map(Menu::getPerms)
                .collect(Collectors.toList());
    }

    @Override
    public List<Menu> listUserMenu(String username) {
        // 获取角色 ID 列表
        List<Long> roleIds = userRoleService.lambdaQuery()
                .inSql(UserRole::getUserId, StrUtil.format("SELECT id FROM t_user WHERE username='{}'", username))
                .list()
                .stream()
                .map(UserRole::getRoleId)
                .collect(Collectors.toList());

        // 获取菜单列表
        List<Long> menuIds = roleMenuService.lambdaQuery()
                .in(RoleMenu::getRoleId, roleIds)
                .list()
                .stream()
                .map(RoleMenu::getMenuId)
                .collect(Collectors.toList());
        if (menuIds.isEmpty()) {
            return new ArrayList<>();
        }

        return lambdaQuery()
                .in(Menu::getId, menuIds)
                .ne(Menu::getPerms, "")
                .isNotNull(Menu::getPerms)
                .list();
    }

    @Override
    public ResultPage<Tree<Menu>> listMenu(QueryMenuReq dto) {
        try {
            Long userId = SystemUtil.getCurrentUser().getId();
            List<Long> menuIds = listMenuIdByUserId(userId);
            List<Menu> menus = lambdaQuery()
                    .in(Menu::getId, menuIds)
                    .eq("0".equals(dto.getType()), Menu::getType, Menu.TYPE_MENU)
                    .orderByAsc(Menu::getOrderNum)
                    .list();

            List<MenuTree> trees = new ArrayList<>();
            buildTrees(trees, menus);
            List<Tree<Menu>> menuTree = (List<Tree<Menu>>) TreeUtil.build(trees);

            return new ResultPage<Tree<Menu>>().setRows(menuTree).setTotal((long) menus.size());
        } catch (Exception e) {
            log.error("查询菜单失败", e);
        }

        return new ResultPage<>();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createMenu(Menu menu) {
        setMenu(menu);
        save(menu);

        // 增加给当前角色
        Long userId = SystemUtil.getCurrentUser().getId();
        List<Long> roleIds = userRoleService.lambdaQuery()
                .eq(UserRole::getUserId, userId)
                .list()
                .stream()
                .map(UserRole::getRoleId)
                .toList();

        roleIds.forEach(roleId -> {
            RoleMenu roleMenu = new RoleMenu();
            roleMenu.setRoleId(roleId);
            roleMenu.setMenuId(menu.getId());
            roleMenuService.save(roleMenu);
        });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateMenu(Menu menu) {
        setMenu(menu);
        updateById(menu);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteMenuByIds(List<String> menuIds) {
        delete(menuIds);
    }

    @Override
    public List<Long> listMenuIdByUserId(Long userId) {
        List<Long> roleIds = userRoleService.lambdaQuery()
                .eq(UserRole::getUserId, userId)
                .list()
                .stream()
                .map(UserRole::getRoleId)
                .collect(Collectors.toList());
        if (roleIds.isEmpty()) {
            return new ArrayList<>();
        }

        return roleMenuService.lambdaQuery()
                .in(RoleMenu::getRoleId, roleIds)
                .list()
                .stream()
                .map(RoleMenu::getMenuId)
                .collect(Collectors.toList());
    }

    private void buildTrees(List<MenuTree> trees, List<Menu> menus) {
        menus.forEach(menu -> {
            MenuTree tree = new MenuTree();
            tree.setId(menu.getId());
            tree.setParentId(menu.getParentId());
            tree.setMenuName(menu.getMenuName());
            tree.setIcon(menu.getIcon());
            tree.setOrderNum(menu.getOrderNum());
            tree.setPath(menu.getPath());
            tree.setType(menu.getType());
            tree.setPerms(menu.getPerms());
            tree.setCreateTime(menu.getCreateTime());
            trees.add(tree);
        });
    }

    private void setMenu(Menu menu) {
        if (ObjectUtil.isNull(menu.getParentId())) {
            menu.setParentId(Menu.TOP_MENU_ID);
        }

        if (Menu.TYPE_BUTTON.equals(menu.getType())) {
            menu.setPath(null);
            menu.setIcon(null);
            menu.setOrderNum(null);
        }
    }

    private void delete(List<String> menuIds) {
        removeByIds(menuIds);
        roleMenuService.lambdaUpdate()
                .in(RoleMenu::getMenuId, menuIds)
                .remove();

        List<Menu> menus = lambdaQuery()
                .in(Menu::getParentId, menuIds)
                .list();
        if (CollUtil.isNotEmpty(menus)) {
            List<String> menuIdList = new ArrayList<>();
            menus.forEach(menu -> menuIdList.add(StrUtil.toString(menu.getId())));
            delete(menuIdList);
        }
    }

}
